package com.hunny.singleton;

import lombok.extern.slf4j.Slf4j;

/**
 * 惰单例
 *
 * @author Create By Cb
 * @date 2022/04/25
 * @since 2022/4/25 9:50
 * <p>
 * 三重检查锁
 * getInstance() 方法内需要使用两个 if (singleton == null)
 * 假设高并发下，线程A、B 都通过了第一个 if 条件。
 * 若A先抢到锁，new 了一个对象，释放锁，然后线程B再抢到锁，此时如果不做第二个 if 判断，B线程将会再 new 一个对象。
 * 使用两个 if 判断，确保了只有第一次调用单例的时候才会做同步，这样也是线程安全的，同时避免了每次都同步的性能损耗
 * </p>
 * <p>
 * volatile 关键字的作用
 * volatile 的作用主要是禁止指定重排序。
 * 假设在不使用 volatile 的情况下，两个线程A、B，都是第一次调用该单例方法，线程A先执行 singleton = new Singleton()，
 * 但由于构造方法不是一个原子操作，编译后会生成多条字节码指令，由于 JAVA的 指令重排序，可能会先执行 singleton 的赋值操作，
 * 该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用，之后 singleton 便不为空了，但是实际的初始化操作却还没有执行。
 * 如果此时线程B进入，就会拿到一个不为空的但是没有完成初始化的singleton 对象，所以需要加入volatile关键字，禁止指令重排序优化，从而安全的实现单例
 * </p>
 */
@Slf4j
public class IdlerSingleton extends BaseSingleton {

    /**
     * 惰单例
     */
    private static volatile IdlerSingleton idlerSingleton;

    /**
     * 惰单例
     */
    private IdlerSingleton() {
        log.warn("懒汉式单例的对象");
    }

    /**
     * 获得实例
     *
     * @return {@link IdlerSingleton}
     */
    public static synchronized IdlerSingleton getInstance() {
        if (idlerSingleton == null) {
            synchronized (IdlerSingleton.class) {
                if (idlerSingleton == null) {
                    idlerSingleton = new IdlerSingleton();
                }
            }
        }
        return idlerSingleton;
    }
}